# -*- coding: utf-8 -*-
"""
Created on Tue Jul 31 10:39:30 2018

@author: Pecado.Wang from UIBE

该程序通过读取PDF、输出关键词所在页码、输出关键词所在表格从而输出我们想要的数据。为了读取PDF，需同时下载
pdfminer和pdfminer.six，为了方便区分，pdfminer改成了pdfminerhhh。此外，我们利用两种方式即单线程和多线程
程序，在某种形式上多线程运行速度会相对快一点。程序的不足在于读取PDF效率和准确度方面都存在一定的问题，有很
多类型的PDF无法读取，这在一定程度上增加了手工查找的工作量，希望有感兴趣的读者可以改进。
正式运行代码从275行开始，前面都是函数的具体形式定义。
"""
import pdfplumber
import pandas as pd
from pdfminerhhh.pdfparser import PDFParser,PDFDocument
from pdfminerhhh.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminerhhh.converter import PDFPageAggregator
from pdfminerhhh.layout import LTTextBoxHorizontal,LAParams
from pdfminerhhh.pdfinterp import PDFTextExtractionNotAllowed
import logging
import re
import os,requests
from io import StringIO
import json
import xlrd,xlwt
import numpy as np
import time
import traceback
import winsound
from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED
#让警告禁止显示
logging.propagate = False 
logging.getLogger().setLevel(logging.ERROR)
#输出企业代码列表
def getcompanyCd():
    path=r'C:\Users\DELL\Desktop\北京新三板企业ww.xls'
    workbook = xlrd.open_workbook(path)
    sheet1 = workbook.sheet_by_index(0)
    numList = sheet1.col_values(0, 1)
    return numList
#记录出错的企业，方便后期手动查询
def bug_recorder(txt):
    with open("D:\新三板2\%s.txt"%'bugRecord_1',"a") as f:
        f.write(txt+'\n')
#根据filepath的名称提取年报的年份
def get_year(filepath):
    ptrn=re.compile('((\d{6})_(\d{4}))')
    year=ptrn.search(filepath).group(3)
    return year
#根据filepath的名称提取公司代码
def get_companyCd(filepath):
    ptrn=re.compile('((\d{6})_(\d{4}))')
    companyCd=ptrn.search(filepath).group(2)
    return companyCd
#返回企业的2015——2017年报下载链接
def pdfurl(companyCd,page_num=1):
    url = 'http://www.neeq.com.cn/disclosureInfoController/infoResult.do?callback=jQuery18309654062013135463_1505374211021'
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
		'Referer': 'http://www.neeq.com.cn/disclosure/announcement.html',
		'Origin': 'http://www.neeq.com.cn'}
    page_num = page_num#设置爬取页数，默认为1
    linkdict={}
    for num in range(int(page_num)):
        data = {'disclosureType':'1',#type1为报告
			 'page':'%d' % num,
			 'isNewThree':'1',
			 'startTime':'2016-01-01',#修改年报下载起始日期
			 'endTime':'2018-08-02',#修改年报下载最终日期
            'companyCd': companyCd,
            'keyword': '年度报告'}#年度报告查询关键字，可更改，如’年度报告摘要’
        req = requests.post(url=url, headers=headers, data=data)
        html = json.loads(req.text[41:][:-1])
        for each in html[0]['listInfo']['content']:
            link='http://www.neeq.com.cn'+each['destFilePath']
            year=re.search("(\d+)",each['disclosureTitle']).group(0)
            if ('摘要' in each['disclosureTitle'] or '半' in each['disclosureTitle']):
              continue
            linkdict[companyCd+'_'+year]=link
        return linkdict
#保存企业年报到本地   
def savePdf(url,pdf_name):
    FILE_DIR='D:\新三板2'
    req = requests.get(url,stream=True)
    if not os.path.exists(FILE_DIR):
        os.makedirs(FILE_DIR)
    filepath=os.path.join(FILE_DIR,pdf_name+'.pdf')
    if os.path.exists(filepath):
        print(pdf_name+'已存在!')
    else:
        with open(filepath,"wb") as pdf_file:
            for content in req.iter_content():
                pdf_file.write(content)
            print(FILE_DIR+'\\'+pdf_name+'下载成功！')
    return filepath
#输出企业2015——2017年的技术人员期初期末人数，没有则为空
def jishurenyuan(index,filepath,keyword):
        result={}
        A=[]
        Result=[]
        company=get_companyCd(filepath)
        year=get_year(filepath)
        for i in index.values():
            for ii in i:
                pdf=pdfplumber.open(filepath)
                p=pdf.pages[int(ii)-1]
                table = p.extract_tables()
                for T in table:
                    for t in T:
                        if "".join(keyword) in t:
                            Result=t
                            A=[i for i in Result if i not in [None]]
                            result[year+"期初"+"".join(keyword)]=A[1]
                            result[year+"期末"+"".join(keyword)]=A[2]
                            print(company+" "+year+'年'+'技术人员信息录入成功!')
                            return result
                            break
            else: #注意else的位置，需要退出整个循环而不只是内部循环                
                result[year+"期初"+"".join(keyword)]=''
                result[year+"期末"+"".join(keyword)]=''
                print(company+" "+year+'年'+'技术人员信息缺失')
                return result
#输出企业2015——2017年的高级管理人员人数，没有则为空
def gaojiguanli(index,filepath,keyword):
        result={}
        A=[]
        company=get_companyCd(filepath)
        year=get_year(filepath)
        for i in index.values():
            for ii in i:
                pdf=pdfplumber.open(filepath)
                p=pdf.pages[int(ii)-1]
                table = p.extract_tables()
                for T in table:
                    for t in T:
                        A=[i for i in t if i not in [None]]
                        for j in A:
                            if "".join(keyword)+":"==j or "".join(keyword)+"："==j:
                                result[year+"".join(keyword)]=A[1]
                                print(company+" "+year+'年'+'高管人员信息录入成功!')
                                return result
                            elif "".join(keyword) in j:
                                result[year+"".join(keyword)]="".join(re.findall('(.*)\n.*',j))
                                print(company+" "+year+'年'+'高管人员信息录入成功!')
                                return result
                            break
            else: #注意else的位置，需要退出整个循环而不只是内部循环                
                result[year+"".join(keyword)]=''
                print(company+" "+year+'年'+'高管人员信息缺失')
                return result
 #返回关键字所在页码，组成一个集合                                              
def get_page(filepath,keyword):
    start=time.clock()
    fp = open(filepath, 'rb')
    #创建一个pdf文档分析器
    parser = PDFParser(fp)
    #创建一个PDF文档对象存储文档结构
    document = PDFDocument()
    # 连接分析器 与文档对象
    parser.set_document(document)
    document.set_parser(parser)
    document.initialize()
    # 检查文件是否允许文本提取
    if not document.is_extractable:
        raise PDFTextExtractionNotAllowed
    else:
        # 创建一个PDF资源管理器对象来存储共赏资源
        rsrcmgr=PDFResourceManager()
        # 设定参数进行分析
        laparams=LAParams()
        # 创建一个PDF设备对象
        # device=PDFDevice(rsrcmgr)
        device=PDFPageAggregator(rsrcmgr,laparams=laparams)
        # 创建一个PDF解释器对象
        interpreter=PDFPageInterpreter(rsrcmgr,device)
        # 处理每一页
        keylen=len(keyword)
        for m in range(keylen):
            exec('page%s=[]'%(m+1))
            exec('pattern%s = re.compile(keyword[m])'%(m+1))
            
        i = 0
        for page in document.get_pages():
            interpreter.process_page(page)
            # # 接受该页面的LTPage对象
            layout=device.get_result()   # return text image line curve
            for x in layout:
                if isinstance(x,LTTextBoxHorizontal):
                    for p in range(keylen):
                        if eval('pattern'+str(p+1)).search(x.get_text()):
                            eval('page'+str(p+1)).append(i+1)
            i +=1
            
        index={}
        for q in range(keylen):
            index[keyword[q]]=list(set(eval('page'+str(q+1))))
        device.close()
        end=time.clock()
        print('PDF parsing time: %s Seconds.'%(end-start))#计算整个Pdf读取时间
        return index
#分析主程序
def get_result(company):
    final_result=pd.DataFrame()
    fp1=fp(company)
    rst={}
    Rst={}
    rst_1={}
    Rst_1={}
    for k,filepath in fp1.items():
        year=get_year(filepath)
        print('正在读取 '+company+" "+year+'年的年报')
        try:
            index=get_page(filepath,keyword)#得到每个关键字的页码
            #rst=jishurenyuan(index,filepath,keyword)#分析技术人员
            rst=gaojiguanli(index,filepath,keyword)#分析高级管理人员
            Rst=dict(rst,**Rst)
            #result=pd.DataFrame(rst,index=[company])
            #final_result=pd.concat([final_result,result],axis=1)
        except Exception as e:
            #rst_1[year+"期初"+"".join(keyword)]=''
            #rst_1[year+"期末"+"".join(keyword)]=''
            rst_1[year+"".join(keyword)]=''
            print(company+" "+year+"年报读取失败!")
            Rst_1=dict(rst_1,**Rst_1)
            #result_1=pd.DataFrame(rst_1,index=[company])
            #final_result_1=pd.concat([final_result_1,result_1],axis=1)
            bug_recorder('第'+str(numList.index(company))+'个:'+str(company)+" "+year+"年报读取失败!")
            print(Exception,":",e)
            #traceback.print_exc()# 利用traceback模块查看异常，内容比较详细
        else:
            print(company+" "+year+"年报读取成功!")
    final=dict(Rst,**Rst_1)#形成合并字典，去除重复键值对
    #Final_result=final_result.join(final_result_1,on=None)
    final_result=pd.DataFrame(final,index=[company])
    return final_result
#不同企业的数据合并
def df_append(final_df,df_add):
    df=final_df.copy()
    df1=final_df.copy().iloc[-1:]
    df2=df_add.copy()
    if df1.index[0]!=df2.index[0]:
        df=df.append(df2)
    else:
        bug_recorder('最终结果合并出错'+df2.index[0])
    return df
#企业pdf文件位置的集合
def fp(company):
    fp={}
    fp[0]='D:\新三板2\\'+company+'_2017.pdf'
    fp[1]='D:\新三板2\\'+company+'_2016.pdf'
    fp[2]='D:\新三板2\\'+company+'_2015.pdf'
    return fp
#下载企业年报的函数
def download(company):
    linkdict={}
    try:
        linkdict=pdfurl(str(company))
    except:
        bug_recorder(str(company))
    for k,v in linkdict.items():
        print('正在下载 '+k+' 之年报')
        filepath=savePdf(v,k)
        print(k+' 之年报下载成功！')
#分析企业年报的函数        
def analyse(company):    
    try:
        result=get_result(company)
        r[company]=result
    except:
        print(company+'数据为空，尴尬')
        time.sleep(1)




#*************正式运行代码*************#
##step1：开始下载企业年报##        
numList=getcompanyCd()
a=input('输入起始位置'+' 应大于等于0'+'\n')
b=input('输入结束位置'+' 应小于1568'+'\n')
company_list=numList[int(a):int(b)]
keyword=['高级管理人员人数']
#使用单线程运行下载程序，并计算总时长 (不知道为什么会莫名其妙出错)
print('正在下载'+str(a)+'~'+str(b)+'区间的年报')       
print('*'*50)        
t3=time.clock()
for company in company_list:
    try:
        linkdict=pdfurl(str(company))
    except:
        bug_recorder(str(company))
    for k,v in linkdict.items():
        print('正在下载 '+k+' 之年报')
        filepath=savePdf(v,k)#保存pdf
        print(k+' 之年报下载成功！')
t4=time.clock()
print('使用单线程下载年报，总共耗时：%smin.'%((t4-t3)/60))
print(str(a)+'~'+str(b)+'区间的年报下载成功!')       
print('*'*50)
winsound.Beep(333, 2500)#程序运行结束时发出蜂鸣提示音，下同
#使用多线程运行下载程序，并计算总时长
print('正在下载'+str(a)+'~'+str(b)+'区间的年报')
print('*'*50)
t1=time.clock()
executor=ThreadPoolExecutor(max_workers=10)
future_tasks=[executor.submit(download,company) for company in company_list]
wait(future_tasks,return_when=ALL_COMPLETED)
t2=time.clock()
print('使用多线程下载年报，总共耗时：%smin.'%((t2-t1)/60))
print(str(a)+'~'+str(b)+'区间的年报下载成功!')       
print('*'*50)
winsound.Beep(333, 2500)
##############################################
##step2：开始分析企业年报##        
print('正在分析'+str(a)+'~'+str(b)+'区间的年报')
final_df=pd.DataFrame(index=['0'])
r={}
#使用单线程运行分析程序，并计算总时长
print('*'*50)        
t3_1=time.clock()
for company in company_list:
    try:
        result=get_result(company)
        r[company]=result
    except:
        print('数据为空，尴尬')
        time.sleep(1)
        continue
t4_1=time.clock()
print('使用单线程分析年报，总共耗时：%shour.'%((t4_1-t3_1)/3600))
print(str(a)+'~'+str(b)+'区间的年报分析完成!')
print('*'*50)
winsound.Beep(333, 2500)
#使用多线程运行分析程序，并计算总时长
print('*'*50)
t1_1=time.clock()
executor=ThreadPoolExecutor(max_workers=10)
future_tasks=[executor.submit(analyse,company) for company in company_list]
wait(future_tasks,return_when=ALL_COMPLETED)
t2_1=time.clock()
print('使用多线程分析年报，总共耗时：%smin.'%((t2_1-t1_1)/60))
print(str(a)+'~'+str(b)+'区间的年报分析完成!')
print('*'*50)
winsound.Beep(333, 2500)
#step3：将每个企业的Dataframe合并，形成总的Dataframe  
for k,v in r.items():
        final_df=df_append(final_df,v)
final_df=final_df.drop(['0'])
#step4：将Dataframe转化成Excel并输出    
from pandas import ExcelWriter
writer = ExcelWriter(str(a)+'~'+str(b)+'_1.xlsx')
final_df.to_excel(writer,'Sheet1')
writer.save()
print('已保存'+str(a)+'~'+str(b)+'区间的分析结果：'+str(a)+'~'+str(b)+'.xlsx\n')




#**************代码调试专用**************#
#在这里你可以调试具体函数是否正常运行，
#主要有pdfurl函数、get_page函数、jishurenyuan函数、gaojiguanli函数、
#以及get_resultl函数#
filepath='d:\\新三板2\\430004_2016.pdf'
keyword=['高级管理人员人数']
result1=pdfurl('430002',1)
result2=get_page(filepath,keyword)           
result3=jishurenyuan(result2,filepath,keyword) 
result5=gaojiguanli(result2,filepath,keyword)       
result4=get_result('430004')                                
pdf=pdfplumber.open(filepath)
p=pdf.pages[int(37)-1]
table = p.extract_tables()
for T in table:
    for t in T:
        A=[i for i in t if i not in [None]]
#***************************************#